﻿#include "Decoder.h"
#include "qsize.h"
#include <QDebug>

extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/avutil.h>
#include <libavutil/pixdesc.h>
}

#include "AudioFilter.h"
#include "VideoFrameFilter.h"

#include <QCoreApplication>
#include <QFile>
#include <QTimer>

#include <QString>
#include <QElapsedTimer>

Q_DECLARE_METATYPE(std::chrono::steady_clock::time_point)

Decoder::Decoder(QObject *parent)
    : QObject{parent}
{
}

Decoder::~Decoder()
{
    stop();
}

bool Decoder::start(const QUrl& url)
{
    if(!url.isLocalFile())
        return false;

    QString file = url.toLocalFile();

    m_file = file;
    if(m_readThread)
        stop();

    if(!initDecodecContexts())
        return false;

    // 创建并启动读取线程
    m_readThread = QThread::create(&Decoder::readThread, this);
    m_readThread->start();

    // 启动所有已经准备好的解码线程
    for(auto&ctx : m_decCtxs) {
        if(ctx.td)
            ctx.td->start();
    }


    setIsRunning(true);

    qDebug() << "The decoder has started";

    return true;
}

void Decoder::setVideoFrameFormat(QSize size, AVPixelFormat fmt)
{
    if(isRunning()) {
        qWarning() << "The output format cannot be modified at runtime";
        return;
    }

    m_videoFrameSize = size;
    m_videoFrameFormat = fmt;
}

void Decoder::setVideoFrameQueueSize(int size)
{
    m_videoFrameQueueSize = size;
}

int Decoder::decodedAudioFrameCount()
{
    if(m_audioStreamIndex == -1)
        return 0;

    return m_decCtxs[m_audioStreamIndex].frameQueue->size();
}

int Decoder::decodedVideoFrameCount()
{
    if(m_videoStreamIndex == -1)
        return 0;

    return m_decCtxs[m_videoStreamIndex].frameQueue->size();
}

void Decoder::dumpMediaInformation()
{
    qInfo() << "Base stream index: " << m_baseStreamIndex;
    qInfo() << "File duration: " << m_fileDuration << "s";
    qInfo() << "Number of audio streams: " << m_nbAudioStreams;

    if(m_audioStreamIndex != -1) {
        auto& audioCtx = m_decCtxs[m_audioStreamIndex];
        qInfo() << "Current audio Stream: \r\n"
                << "\t       Index: " << m_audioStreamIndex << "\r\n"
                << "\t  Codec type: " << audioCtx.codec->name << "\r\n"
                << "\t Sample rate: " << audioCtx.codecCtx->sample_rate << "\r\n"
                << "\t    Channels: " << audioCtx.codecCtx->channels << "\r\n"
                << "\t    Duration: " << audioCtx.stream->duration * av_q2d(audioCtx.stream->time_base) << "s \r\n"
                << "\t  Start time: " << audioCtx.stream->start_time * av_q2d(audioCtx.stream->time_base) << "s \r\n";
    }

    if(m_videoStreamIndex != -1) {
        auto& videoCtx = m_decCtxs[m_videoStreamIndex];
        qInfo() << "Video Stream: \r\n"
                << "\t        Index: " << m_videoStreamIndex << "\r\n"
                << "\t   Codec type: " << videoCtx.codec->name << "\r\n"
                << "\t  Average FPS: " << av_q2d(videoCtx.stream->avg_frame_rate) << "\r\n"
                << "\t Image format: " << av_get_pix_fmt_name(videoCtx.codecCtx->pix_fmt) << "\r\n"
                << "\t     Duration: " << videoCtx.stream->duration * av_q2d(videoCtx.stream->time_base) << "s \r\n"
                << "\t   Start time: " << videoCtx.stream->start_time * av_q2d(videoCtx.stream->time_base) << "s \r\n";
    }
}

double Decoder::fileDuration()
{
    return m_fileDuration;
}

void Decoder::seek(double timePoint)
{
    if(!m_readThread) {
        qDebug() << "Deocder is idling";
        return;
    }

    // 强制使用 readThread 执行 seek
    if(QThread::currentThread() != m_readThread) {
        QObject* tempObj = new QObject();
        tempObj->moveToThread(m_readThread);
        QMetaObject::invokeMethod(tempObj, std::bind(&Decoder::seek, this, timePoint), Qt::BlockingQueuedConnection);
        tempObj->deleteLater();
        return;
    }

    if(timePoint > fileDuration() || std::isnan(timePoint) || timePoint < 0.0) {
        qWarning() << "Failed to seek file, invalied timepoint: " << timePoint;
        return;
    }

    double retTimePoint;
    int differCount = seekToPreviousKeyframe(m_fmtCtx, m_baseStreamIndex, timePoint, retTimePoint);
    if(differCount == 0) {
        qWarning() << "Failed to seek the video file, ";
        return;
    }

    int64_t newSerial = int64_t(timePoint * 1000000.0);

    // 清理所有流的 packet 队列 和 frame 队列
    for(auto& ctx : m_decCtxs) {
        if(ctx.pktQueue) {
            ctx.pktQueue->clear();
            ctx.frameQueue->setSerial(newSerial);
        }
    }

    emit decodecSerialChanged();

    setIsEnding(false);

    qDebug() << "Seek timestamp, expect: " << timePoint << "s, result: " << retTimePoint << "s";
}


void Decoder::stop()
{
    if(m_readThread) {
        m_readThread->requestInterruption();
        m_readThread->wait();
        delete m_readThread;
        m_readThread = nullptr;
    }

    if(m_fmtCtx) {
        for(int i = 0; i < m_fmtCtx->nb_streams; ++i) {
            if(m_decCtxs[i].td) {
                m_decCtxs[i].td->requestInterruption();
                m_decCtxs[i].td->wait();
                m_decCtxs[i].td->deleteLater();
                m_decCtxs[i].td = nullptr;
                m_decCtxs[i].frameQueue = nullptr;
                m_decCtxs[i].pktQueue = nullptr;
                m_decCtxs[i].frameFilter = nullptr;

                avcodec_free_context(&m_decCtxs[i].codecCtx);
            }
        }

        m_decCtxs.clear();
        avformat_close_input(&m_fmtCtx);
    }

    m_audioStreamIndex = -1;
    m_videoStreamIndex = -1;
    m_baseStreamIndex = -1;
    m_nbAudioStreams = 0;

    setIsRunning(false);
}

bool Decoder::initDecodecContexts()
{
    int ret = 0;

    int videoStreamIndex = -1;
    int audioStreamIndex = -1;

    auto filenameByteArr = m_file.toUtf8();
    auto input_filename = filenameByteArr.constData();

    AVFormatContext *fmt_ctx = avformat_alloc_context();
    if (!fmt_ctx){
        qWarning() << "Could not allocate format context";
        return false;
    }

    // 打开文件
    ret = avformat_open_input(&fmt_ctx, input_filename, nullptr, nullptr);
    if (ret < 0){
        qWarning() << "Could not open input file ";
        avformat_free_context(fmt_ctx);
        return false;
    }

    // 查找流信息
    if (avformat_find_stream_info(fmt_ctx, nullptr) < 0){
        qWarning() << "Could not find stream information";
        avformat_free_context(fmt_ctx);
        return false;
    }

    av_dump_format(fmt_ctx, 0, input_filename, 0);

    m_decCtxs.clear();
    m_decCtxs.resize(fmt_ctx->nb_streams);
    m_fmtCtx = fmt_ctx;

    if(m_fmtCtx->duration <= 0) {
        qWarning() << "Failed to get media duration: " << input_filename;
        avformat_free_context(fmt_ctx);
    }

    // 获取文件视频流和音频流索引
    for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
        } else if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            audioStreamIndex = i;
            ++m_nbAudioStreams;
        }
    }

    if (videoStreamIndex == -1 && audioStreamIndex == -1) {
        qWarning() << "Could not find video or audio stream in the input file";
        return false;
    }

    // 查询音频流的解码器并创建解码上下文
    if (audioStreamIndex >= 0) {
        auto& ctx = m_decCtxs[audioStreamIndex];
        ctx.streamIndex = audioStreamIndex;
        ret = openCodecContext(ctx);
        if(!ret) {
            ctx.frameQueue.reset(new FrameQueue());
            ctx.pktQueue.reset(new PacketQueue());
            AudioFilter* filter = new AudioFilter();
            filter->initFilterGraph(ctx.codecCtx->time_base, ctx.codecCtx->sample_rate, ctx.codecCtx->sample_fmt,
                                    ctx.codecCtx->channel_layout, 44100, AV_SAMPLE_FMT_S16, AV_CH_LAYOUT_STEREO,
                                    "aresample=44100,aformat=sample_fmts=s16:channel_layouts=stereo");
            ctx.frameFilter.reset(filter);

            ctx.td = QThread::create(&Decoder::decodecThread, this, audioStreamIndex);

            m_audioStreamIndex = m_baseStreamIndex = audioStreamIndex;
        }
    }

    // 查询视频流的解码器并创建解码上下文
    if (videoStreamIndex >= 0) {

        auto& ctx = m_decCtxs[videoStreamIndex];
        ctx.streamIndex = videoStreamIndex;
        ret = openCodecContext(ctx);
        if(!ret) {

            VideoFrameFilter* filter = new VideoFrameFilter();
            ctx.frameQueue.reset(new FrameQueue());
            ctx.pktQueue.reset(new PacketQueue());

            QSize outSize = m_videoFrameSize.isValid() ? m_videoFrameSize : QSize(ctx.codecCtx->width, ctx.codecCtx->height);
            filter->init(ctx.codecCtx->width, ctx.codecCtx->height, ctx.codecCtx->pix_fmt,
                         outSize.width(), outSize.height(), m_videoFrameFormat);
            ctx.frameFilter.reset(filter);

            ctx.td = QThread::create(&Decoder::decodecThread, this, videoStreamIndex);
            m_videoStreamIndex = videoStreamIndex;

            if(m_baseStreamIndex == -1)
                m_baseStreamIndex = videoStreamIndex;
        }
    }

    if(m_baseStreamIndex == -1)
        return false;

    //m_fileDuration = m_decCtxs[m_baseStreamIndex].stream->duration * av_q2d(m_decCtxs[m_baseStreamIndex].stream->time_base);
    m_fileDuration = m_fmtCtx->duration / 1000000.0;
    return true;
}

void Decoder::setIsEnding(bool b)
{
    if(m_isEnding != b) {
        m_isEnding = b;
        emit isEndingChanged();
    }
}

QString Decoder::videoFile()
{
    return m_file;
}

int Decoder::seekToPreviousKeyframe(AVFormatContext* fmtCtx, int baseStreamIndex, double position, double& result) {
    if (!fmtCtx || baseStreamIndex < 0 || baseStreamIndex >= fmtCtx->nb_streams) {
        qCritical() << "Invailed arguments";
        return 0;
    }

    AVStream* baseStream = fmtCtx->streams[baseStreamIndex];
    AVRational avTimeBase;
    avTimeBase.den = 1;
    avTimeBase.num = 1000000;

    // Convert the position to the time base of the base stream
    int64_t targetTimestamp = int64_t(position / av_q2d(baseStream->time_base));

    // Seek to the closest keyframe before the target timestamp
    int ret = avformat_seek_file(fmtCtx, baseStreamIndex, INT64_MIN, targetTimestamp,
                                 targetTimestamp, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
    if (ret < 0) {
        qWarning() << "Failed to seek video file. errCoder: " << ret;
        return 0;
    }

    return 1;
}

int Decoder::openCodecContext(DecodecContext &ctx)
{
    ctx.stream = m_fmtCtx->streams[ctx.streamIndex];
    ctx.codec = avcodec_find_decoder(ctx.stream->codecpar->codec_id);
    if (!ctx.codec) {
        qWarning() << "Could not find codec, streamIndex: " << ctx.streamIndex;
        return -1;
    }

    ctx.codecCtx = avcodec_alloc_context3(ctx.codec);
    if (!ctx.codecCtx) {
        qWarning() << "Could not allocate codec context, streamIndex: " << ctx.streamIndex;
        return -1;
    }

    if (avcodec_parameters_to_context(ctx.codecCtx, ctx.stream->codecpar) < 0) {
        qWarning() << "Could not copy codec parameters to codec context, streamIndex: " << ctx.streamIndex;
        return -1;
    }

    if(ctx.stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
        ctx.codecCtx->thread_count = 0;
    }

    if (avcodec_open2(ctx.codecCtx, ctx.codec, nullptr) < 0) {
        qWarning() << "Could not open codec, streamIndex: " << ctx.streamIndex;
        return -1;
    }

    return 0;
}

void Decoder::readThread()
{
    int ret = 0;

    while(!m_readThread->isInterruptionRequested()) {
        qApp->processEvents();

        // 检查数据包队列是否已满
        int64_t pktQueuesBytesSize = 0;
        if(m_audioStreamIndex != -1 && m_decCtxs[m_videoStreamIndex].pktQueue)
            pktQueuesBytesSize += m_decCtxs[m_audioStreamIndex].pktQueue->bytesSize();

        if(m_videoStreamIndex != -1 && m_decCtxs[m_videoStreamIndex].pktQueue)
            pktQueuesBytesSize += m_decCtxs[m_videoStreamIndex].pktQueue->bytesSize();

        if (pktQueuesBytesSize > 1024 * 16 * 5 + 1024 * 256 * 5) {
            QThread::msleep(15);
            continue;
        }

        AVPacket* pkt = av_packet_alloc();
        if (!pkt) {
            qWarning() << "Could not alloc packet";
            break ;
        }

        ret = av_read_frame(m_fmtCtx, pkt);
        if(ret < 0) {
            if(ret == AVERROR_EOF) {
                av_packet_free(&pkt);
                setIsEnding(true);
                QThread::yieldCurrentThread();
                continue;
            }
            
            qWarning() << "An error occurred when reading audio track" << ret;
            av_packet_free(&pkt);
            break;
        }

        if(m_decCtxs[pkt->stream_index].pktQueue) {
            m_decCtxs[pkt->stream_index].pktQueue->push(pkt);
        }
        else {
            av_packet_free(&pkt);
        }
    }

    setIsRunning(false);

    qInfo() << "The read thread is exiting";
}

void Decoder::decodecThread(int streamIndex)
{
    int ret = 0;
    auto& ctx = m_decCtxs[streamIndex];
    auto& pktQueue = ctx.pktQueue;
    auto& frameQueue = ctx.frameQueue;
    DecodecSerial currentSerial = 0;

    while(!ctx.td->isInterruptionRequested()) {
        qApp->processEvents(QEventLoop::AllEvents, 30);

        // seek 后处理
        if(ctx.frameQueue->serial() != currentSerial) {
            avcodec_flush_buffers(ctx.codecCtx);
            currentSerial = ctx.frameQueue->serial();
        }

        int frameQueueMax = 10;
        if(ctx.codecCtx->codec_type == AVMEDIA_TYPE_VIDEO)
            frameQueueMax = m_videoFrameQueueSize;
        else if(ctx.codecCtx->codec_type == AVMEDIA_TYPE_AUDIO)
            frameQueueMax = 80;

        if (frameQueue->size() > frameQueueMax || pktQueue->queueSize() == 0) {
            QThread::yieldCurrentThread();
            continue;
        }

        auto pkt = pktQueue->pop();
        if (pkt->size == 0) {
            av_packet_free(&pkt);
        }

        // 发送packet到解码器
        ret = avcodec_send_packet(ctx.codecCtx, pkt);
        av_packet_free(&pkt);
        if(ret == AVERROR(EAGAIN)) {
            continue;
        }
        else if( ret == AVERROR_EOF) {
            qInfo() << "The stream has been decoded to the end, index： " << ctx.streamIndex;
            break ;
        }
        else if( ret < 0) {
            qInfo() << "An error occurred when decoding stream index: " << ctx.streamIndex << "errCode: " << ret;
            break ;
        }

        // 从解码器获取解码后的frame
        AVFrame* frame = av_frame_alloc();

        for(;;) {
            ret = avcodec_receive_frame(ctx.codecCtx, frame);
            if(ret == AVERROR(EAGAIN)) {
                ret = 0;
                break;
            }
            else if (ret == AVERROR_EOF) {
                qInfo() << "The stream has been decoded to the end, index： " << ctx.streamIndex;
                break;
            }
            else if (ret < 0) {
                qInfo() << "An error occurred when decoding stream index: " << ctx.streamIndex << "errCode: " << ret;
                break;
            }

            if(streamIndex == m_baseStreamIndex)
                m_currPTS = frame->pts;

            int64_t frameStartTime = int64_t((frame->pts * av_q2d(ctx.stream->time_base)) * 1000000.0);
            int64_t frameEndTime = int64_t(((frame->pkt_duration + frame->pts) * av_q2d(ctx.stream->time_base)) * 1000000.0);
            if(frameEndTime <= currentSerial.timepoint) {
                // Discard this frame
                continue;
            }

            auto filteredFrame = ctx.frameFilter->filter(frame);
            if(filteredFrame) {
                frameQueue->push(filteredFrame);
            }
        }

        if (ret < 0)
            break;

        av_frame_free(&frame);
    }

    qDebug() << "The decoding thread is ending, streamIndex: " << streamIndex;
}

DecodecSerial Decoder::decodecSerial() const
{
    if(m_baseStreamIndex == -1)
        return 0;

    return m_decCtxs[m_baseStreamIndex].frameQueue->serial();
}

bool Decoder::isEnding() const
{
    return m_isEnding;
}

bool Decoder::isRunning() const
{
    return m_isRunning;
}

AVRational Decoder::videoTimebase()
{
    if(!isRunning() || m_videoStreamIndex == -1)
        return AVRational{0, 1};

    return m_decCtxs[m_videoStreamIndex].stream->time_base;
}

AVFrame *Decoder::nextVideoFrame(int timeout)
{
    if(m_videoStreamIndex == -1)
        return nullptr;

    return m_decCtxs[m_videoStreamIndex].frameQueue->pop(timeout);
}

int Decoder::videoFrameQueueSize()
{
    if(m_videoStreamIndex == -1)
        return -1;

    return m_decCtxs[m_videoStreamIndex].frameQueue->size();
}

AVFrame *Decoder::nextAudioFrame()
{
    if(audioFrameQueueSize() <= 0)
        return nullptr;

    return m_decCtxs[m_audioStreamIndex].frameQueue->pop();
}

int Decoder::audioFrameQueueSize()
{
    if(m_audioStreamIndex == -1)
        return -1;

    return m_decCtxs[m_audioStreamIndex].frameQueue->size();
}

void Decoder::setIsRunning(bool b)
{
    if (m_isRunning == b)
        return;

    m_isRunning = b;

    setIsEnding(!b);

    emit isRunningChanged();
}
